//
// request_handler.cpp
// ~~~~~~~~~~~~~~~~~~~
//

#include <fstream>
#include <sstream>
#include <string>
#include <boost/lexical_cast.hpp>
#include "mime_types.hpp"
#include "reply.hpp"
#include "request.hpp"
#include "request_handler.hpp"
#include "connection.hpp"
#include "server.hpp"
#include "lru_cache_using_boost.hpp"

namespace http {
namespace server3 {

request_handler::request_handler() :
	lua_interface_() {
}

void request_handler::handle_request(boost::shared_ptr<request> req,
		boost::shared_ptr<reply> rep) {
	// TODO: change to fast_url_decode
	std::string &request_path = req->uri;
	std::string webqueue_status = "/status";
	std::string load_balance_status = "/check.html";

	// Request path must be absolute and not contain "..".
	if (request_path.empty() || request_path[0] != '/' || request_path.find(
			"..") != std::string::npos) {
		rep = reply::stock_reply(reply::bad_request);
	} else if (request_path.substr(0, webqueue_status.size())
			== webqueue_status) {
		handle_status(*rep.get());
	} else if (request_path.substr(0, load_balance_status.size())
			== load_balance_status) {
		handle_check_alive(*rep.get());
	} else {

#ifdef TEST_SERVER_NO_LUA
		rep->headers.resize(2);
		rep->status = reply::ok;
		rep->headers[0].name = "Content-Length";
		rep->headers[0].value = "0";
		rep->headers[1].name = "Content-Type";
		rep->headers[1].value = "image/gif";
#else
		//If this request is forwarded from balancing server.
        __sync_fetch_and_add(&server::total_request,1);
		std::string forward_ip;
		if (req->get_header_value("X-Forwarded-For", forward_ip)) {
			if(forward_ip.find("unknown",0) != string::npos)
			{
				//std::cout <<"forward_ip contains unknown"<<std::endl;
	            __sync_fetch_and_add(&server::unknown_ip,1);
	            handle_ok(*rep.get());
				return;
			}
			//todo: get the first ip of the ip chain.
			size_t found = forward_ip.find(',');
			if (found != string::npos) {
				forward_ip = forward_ip.substr(0, found);
			}
			forward_ip.erase(std::remove(forward_ip.begin(), forward_ip.end(),' '), forward_ip.end());
			req->ip = forward_ip;
		}

		lua_interface_.do_business_interface(*req.get(), *rep.get());
		rep->add_header("Content-Length",
				boost::lexical_cast<std::string>(rep->content.length()));

		if (rep->status != 200 && rep->status != 302) {
	        __sync_fetch_and_add(&server::error_request,1);
	        handle_ok(*rep.get());
			std::cout << "ERROR Replay: status = " << rep->status << std::endl;
			std::cout << "\tRequest.ip  = " << req->ip << std::endl;
			std::cout << "\tRequest.uri = " << req->uri << std::endl;
			return;
		}

#endif
	}
}
void request_handler::handle_ok(reply &rep){
	rep.headers.resize(2);
	rep.status = reply::ok;
	rep.headers[0].name = "Content-Length";
	rep.headers[0].value = "0";
	rep.headers[1].name = "Content-Type";
	rep.headers[1].value = "image/gif";
	return;
}
void request_handler::handle_check_alive(reply &rep) {
	rep.headers.resize(2);
	rep.status = reply::ok;
	rep.content = "<html>"
		"<body>alive</body>"
		"</html>";
	rep.headers[0].name = "Content-Length";
	rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
	rep.headers[1].name = "Content-Type";
	rep.headers[1].value = "text/html";
	return;
}
void request_handler::handle_status(reply& rep) {

	std::map<std::string,int>::iterator it;
	typedef std::pair<std::string, int> map_pair;
	it = server::business_count_map.find("zc.1");
	int zc_1_count = -1;
	if (it != server::business_count_map.end()){
		zc_1_count = it->second;
	}
	rep.headers.resize(2);
	rep.status = reply::ok;
	rep.content = "<html>"
		"<head><title>Status</title></head>"
		"<body>"
		"<h3>Number of concurrent connection: ";
	rep.content.append(
			boost::lexical_cast<std::string>(connection::connection_num));

	rep.content.append("</h3>"
		"<h3>Process request rate: ");
	rep.content.append(boost::lexical_cast<std::string>(server::request_rate));

	rep.content.append("</h3>"
		"<h3>Unknown request ip: ");
	rep.content.append(boost::lexical_cast<std::string>(server::unknown_ip));

	rep.content.append("</h3>"
		"<h3>Total request: ");
	rep.content.append(boost::lexical_cast<std::string>(server::total_request));

	rep.content.append("</h3>"
		"<h3>Error request: ");
	rep.content.append(boost::lexical_cast<std::string>(server::error_request));

	rep.content.append("</h3>"
		"<h3>Parsing error: ");
	rep.content.append(boost::lexical_cast<std::string>(server::parsing_error));

	rep.content.append("</h3>"
		"<h3>Received ZC1: ");
	rep.content.append(boost::lexical_cast<std::string>(server::received_zc1));

	rep.content.append("</h3>"
		"<h3>Processed ZC1: ");
	rep.content.append(boost::lexical_cast<std::string>(server::processed_zc1));

	rep.content.append("</h3>"
		"<h3>ZC1 pushed on redis: ");
	rep.content.append(boost::lexical_cast<std::string>(zc_1_count));

	rep.content.append("</h3>"
		"<h3>Cache hit: ");
	rep.content.append(boost::lexical_cast<std::string>(server::cache_hit));

	rep.content.append("</h3>"
		"<h3>Cache miss: ");
	rep.content.append(boost::lexical_cast<std::string>(server::cache_miss));

	rep.content.append("</h3>"
		"<h3>Start Time: ");
	rep.content.append(server::start_time);

	rep.content.append("</h3>"
		"</body>"
		"</html>");
	rep.headers[0].name = "Content-Length";
	rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
	rep.headers[1].name = "Content-Type";
	rep.headers[1].value = "html";
	return;
}

bool request_handler::url_decode(const std::string& in, std::string& out) {
	out.clear();
	out.reserve(in.size());
	for (std::size_t i = 0; i < in.size(); ++i) {
		if (in[i] == '%') {
			if (i + 3 <= in.size()) {
				int value = 0;
				std::istringstream is(in.substr(i + 1, 2));
				if (is >> std::hex >> value) {
					out += static_cast<char> (value);
					i += 2;
				} else {
					return false;
				}
			} else {
				return false;
			}
		} else if (in[i] == '+') {
			out += ' ';
		} else {
			out += in[i];
		}
	}
	return true;
}
} // namespace server3
} // namespace http
